package cn.ibizlab.core.open.justauth;

import cn.ibizlab.util.common.JsonFieldExtractor;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.utils.HttpUtils;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.*;

public class AuthCustomRequest extends AuthDefaultRequest {
    public AuthCustomRequest(AuthConfig config, AuthCustomSource source) {
        super(config, source);
    }

    public AuthCustomSource getSource() {
        return (AuthCustomSource)this.source;
    }

    public Map<String, String> buildAuthParams(String state,String code,String token,String refreshToken) {
        Map<String, String> params = new HashMap<>();
        params.put("client_id", config.getClientId());
        params.put("client_secret", config.getClientSecret());
        params.put("redirect_uri", config.getRedirectUri());
        params.put("state", state);
        params.put("code", code);
        params.put("token", token);
        params.put("refresh_token",refreshToken);
        return params;
    }

    @Override
    public String authorize(String state) {
        if (source.authorize()!=null && source.authorize().indexOf("?")>0)
            return buildUrl(source.authorize(), buildAuthParams(state,"","",""));
        return super.authorize(state);
    }

    @Override
    protected String accessTokenUrl(String code) {
        if (source.accessToken()!=null && source.accessToken().indexOf("?")>0)
            return buildUrl(source.accessToken(), buildAuthParams("",code,"",""));
        return super.accessTokenUrl(code);
    }

    @Override
    protected String refreshTokenUrl(String refreshToken) {
        if (source.refresh()!=null && source.refresh().indexOf("?")>0)
            return buildUrl(source.refresh(), buildAuthParams("","","",refreshToken));
        return super.refreshTokenUrl(refreshToken);
    }

    @Override
    protected String userInfoUrl(AuthToken authToken) {
        if (source.userInfo()!=null && source.userInfo().indexOf("?")>0)
            return buildUrl(source.userInfo(), buildAuthParams("",authToken.getCode(),authToken.getAccessToken(),authToken.getRefreshToken()));
        return super.userInfoUrl(authToken);
    }

    @Override
    protected String revokeUrl(AuthToken authToken) {
        if (source.revoke()!=null && source.revoke().indexOf("?")>0)
            return buildUrl(source.revoke(), buildAuthParams("",authToken.getCode(),authToken.getAccessToken(),authToken.getRefreshToken()));
        return super.revokeUrl(authToken);
    }

    @Override
    protected AuthToken getAccessToken(AuthCallback authCallback) {
        String response = "POST".equalsIgnoreCase(this.getSource().getAccessTokenMethod()) ? this.doPostAuthorizationCode(authCallback.getCode()):this.doGetAuthorizationCode(authCallback.getCode());
        JSONObject accessTokenObject = JSONObject.parseObject(response);
        this.checkResponse(accessTokenObject);
        JSONObject extracted = JsonFieldExtractor.extractJSON(accessTokenObject, getSource().getMapping());
        return AuthToken.builder().accessToken(extracted.getString("access_token"))
                .refreshToken(extracted.getString("refresh_token"))
                .scope(extracted.getString("scope"))
                .tokenType(extracted.getString("token_type"))
                .expireIn(extracted.getIntValue("expires_in")).code(authCallback.getCode()).build();
    }

    protected String doPostUserInfo(AuthToken authToken) {
        return (new HttpUtils(this.config.getHttpConfig())).post(this.userInfoUrl(authToken),"{}").getBody();
    }

    @Override
    protected AuthUser getUserInfo(AuthToken authToken) {
        String userInfo = "GET".equalsIgnoreCase(this.getSource().getUserInfoMethod()) ? this.doGetUserInfo(authToken):this.doPostUserInfo(authToken);
        JSONObject object = JSONObject.parseObject(userInfo);
        this.checkResponse(object);
        JSONObject extracted = JsonFieldExtractor.extractJSON(object, getSource().getMapping());
        return AuthUser.builder().rawUserInfo(extracted).uuid(extracted.getString("uid"))
                .username(extracted.getString("display_name"))
                .avatar(extracted.getString("avatar"))
                .company(extracted.getString("organization_id"))
                .location(extracted.getString("postal_address"))
                .email(extracted.getString("mail"))
                .token(authToken).source(this.source.toString()).build();
    }

    private void checkResponse(JSONObject object) {
        if (!ObjectUtils.isEmpty(this.getSource().getSuccessFlagMapping())) {
            this.getSource().getSuccessFlagMapping().forEach((key,condVal)->{
                if(!JsonFieldExtractor.extractJSON(object,key).equalsIgnoreCase((String)condVal))
                    throw new AuthException(object.getString("error_description"));
            });

        }
    }


    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(\\w+)\\}");

    /**
     * 模板渲染核心方法
     * @param template URL模板，如 "http://xxx/authorize?appkey={client_id}&secukey={client_secret}"
     * @param params   参数键值对，需包含模板所需所有参数
     * @return 渲染完成的URL
     */
    public static String buildUrl(String template, Map<String, String> params) {
        Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String key = matcher.group(1);
            String value = params.getOrDefault(key, "");

            matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
        }
        matcher.appendTail(sb);

        return sb.toString();
    }
}